///////////////////////////////////////////////////////////////////////////
//  Brainfreeze - Yet another sokoban puzzle game, this time in 3D
//  Copyright (C) 2006 Scott MacDonald
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
///////////////////////////////////////////////////////////////////////////
// GameLevel.cpp - code definition for a sokoban game level
#include "StdAfx.h"
#include "Player.h"

///////////////////////////////////////////////////////////////////////////
// Move the object one space to the left. Returns false if we cannot
// move
///////////////////////////////////////////////////////////////////////////
bool Player::MoveLeft(GameLevel& level, bool force=false)
{
	//
	// Start a record of this movement (don't save it if we cannot make
	// the move
	//
	GameMove move;
	move.direction = DIRECTION_LEFT;

	// calculate the position we want to move to
	Ogre::Vector3 newPosition = position;
	newPosition.x--;

	// make sure it is still in bounds
	if( newPosition.x < 0.0 )
	{
		throw new std::exception("Cannot move object left - already at zero");
	}

	// see if there is a box where we want to move. If so, try to move it
	if( level.HasBox(newPosition) )
	{
		// hold on to the box's pointer (for the undo list)
		move.piece = &(level.GetBox(newPosition));

		// attempt to move the box to the left
		level.GetBox(newPosition).MoveLeft(level, false);
	}

	// make sure nothing is blocking the spot we want to move to
	if( (! force) && IsBlocked(level, newPosition) )
	{
		return false;
	}

	//
	// okay, all good. Commit the new position, record it in our undo list, 
	// and let the caller know that it worked
	//
	moveList.push( move );
	position = newPosition;

	return true;
}

///////////////////////////////////////////////////////////////////////////
// Move the object one space to the right. Returns false if we cannot
// move
///////////////////////////////////////////////////////////////////////////
bool Player::MoveRight(GameLevel& level, bool force=false)
{
	//
	// Start a record of this movement (don't save it if we cannot make
	// the move
	//
	GameMove move;
	move.direction = DIRECTION_RIGHT;

	// calculate the position we want to move to
	Ogre::Vector3 newPosition = position;
	newPosition.x++;

	// make sure its within level boundaries
	if( newPosition.x > level.Width() -1 )
	{
		throw new std::exception("Cannot move object right - would move past level bound");
	}

	// see if there is a box where we want to move. If so, try to move it
	if( level.HasBox(newPosition) )
	{
		// hold on to the box's pointer (for the undo list)
		move.piece = &(level.GetBox(newPosition));

		// attempt to move the obstructing box right
		level.GetBox(newPosition).MoveRight(level, false);
	}

	// make sure nothing is blocking the spot we want to move to
	if( (! force) && IsBlocked(level, newPosition) )
	{
		return false;
	}

	//
	// okay, all good. Commit the new position, record it in our undo list, 
	// and let the caller know that it worked
	//
	moveList.push( move );
	position = newPosition;

	return true;
}

///////////////////////////////////////////////////////////////////////////
// Move the object one space up. Returns false if we cannot move
///////////////////////////////////////////////////////////////////////////
bool Player::MoveUp(GameLevel& level, bool force=false)
{
	//
	// Start a record of this movement (don't save it if we cannot make
	// the move
	//
	GameMove move;
	move.direction = DIRECTION_UP;

	// calculate the position we want to move to
	Ogre::Vector3 newPosition = position;
	newPosition.y--;

	// make sure its within level boundaries
	if( newPosition.y < 0.0 )
	{
		throw new std::exception("Cannot move object up - would move past level bound");
	}

	// see if there is a box where we want to move. If so, try to move it
	if( level.HasBox(newPosition) )
	{
		// hold on to the box's pointer (for the undo list)
		move.piece = &(level.GetBox(newPosition));

		// attmpt to move the obstructing crate up
		level.GetBox(newPosition).MoveUp(level, false);
	}

	// make sure nothing is blocking the spot we want to move to
	if( (! force) && IsBlocked(level, newPosition) )
	{
		return false;
	}

	//
	// okay, all good. Commit the new position, record it in our undo list, 
	// and let the caller know that it worked
	//
	moveList.push( move );
	position = newPosition;

	return true;
}

///////////////////////////////////////////////////////////////////////////
// Move the object one space down. Returns false if we cannot move
///////////////////////////////////////////////////////////////////////////
bool Player::MoveDown(GameLevel& level, bool force=false)
{
	//
	// Start a record of this movement (don't save it if we cannot make
	// the move
	//
	GameMove move;
	move.direction = DIRECTION_DOWN;

	// calculate the position we want to move to
	Ogre::Vector3 newPosition = position;
	newPosition.y++;

	// make sure its within level boundaries
	if( newPosition.y > level.Height() - 1 )
	{
		throw new std::exception("Cannot move object down - would move past level bound");
	}

	// see if there is a box where we want to move. If so, try to move it
	if( level.HasBox(newPosition) )
	{
		// hold on to the box's pointer (for the undo list)
		move.piece = &(level.GetBox(newPosition));

		// attempt to move the box down
		level.GetBox(newPosition).MoveDown(level, false);
	}

	// make sure nothing is blocking the spot we want to move to
	if( (! force) && IsBlocked(level, newPosition) )
	{
		return false;
	}

	//
	// okay, all good. Commit the new position, record it in our undo list, 
	// and let the caller know that it worked
	//
	moveList.push( move );
	position = newPosition;
	return true;	
}
